# 普通装饰器
def decorator(func):
    def wrapper():
        print("执行装饰器 - 开始")
        func()
        print("执行装饰器 - 结束")

    return wrapper

# 这里的decorator相当于是：work1 = decorator(work1)
@decorator
def work1():
    print("----- 目标函数 -----")

work1()

print("=" * 50)
# 实现一个统计任意函数执行时间的函数
import time

def time_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数 {func.__name__} 执行耗时：{(end_time - start_time):.4f} 秒")
        return result
    return wrapper

# 使用装饰器
@time_decorator
def work2():
    time.sleep(2)
    print("----- 目标函数 -----")
    return "work2执行后返回值"

result = work2()
print(result)

print("=" * 50)
# 装饰器装饰类
def my_decorator(cls):
    def wrapper(*args, **kwargs):
        print("执行装饰器 - 开始")
        instance = cls(*args, **kwargs)
        print("执行装饰器 - 结束")
        return instance
    return wrapper

@my_decorator
class MyClass:
    def __init__(self, name):
        self.name = name

    def say_hello(self):
        print(f"Hello, {self.name}")


my_obj = MyClass("Alice")


print("=" * 50)
# 装饰器传参数
def arg_decorator(name,age):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"执行装饰器 - 开始，参数：{name} {age}")
            result = func(*args, **kwargs)
            print("执行装饰器 - 结束")
            return result
        return wrapper
    return decorator

# 相当于：work3 = arg_decorator("张三",18)(work3)
@arg_decorator("张三",18)
def work3():
    print("----- 目标函数 -----")

work3()

print("=" * 50)
# 类实现装饰器
class Decorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("类执行装饰器 - 开始")
        result = self.func(*args, **kwargs)
        print("类执行装饰器 - 结束")
        return result

def work4():
    print("----- 目标函数 -----")

Decorator(work4)()


print("=" * 50)
# 装饰器的副作用
def decorator1(func):
    def wrapper(*args, **kwargs):
        print("执行装饰器 - 1")
        result = func(*args, **kwargs)
        print("执行装饰器 - 1")
        return result
    return wrapper

@decorator1
def work5(a,b):
    """
    实现两个数相加的函数
    :param a: 数字1
    :param b: 数字2
    :return: 返回两个数相加的结果
    """
    return a + b

# 这里这样运行是没有问题的
result = work5(1,2)
print(result)   # 3
# 现在我们要访问函数属性（__doc__,__name__）有问题
# 因为装饰器上相当于 work5 = decorator1(work5),则decorator1()返回的是wrapper，所以这下面这里取的是wrapper的属性
print(work5.__doc__)
print(work5.__name__)

print("=" * 50)
# 解决装饰器的副作用
from functools import wraps
def decorator2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("执行装饰器 - 2")
        result = func(*args, **kwargs)
        print("执行装饰器 - 2")
        return result
    return wrapper

@decorator2
def work6(a,b):
    """
    实现两个数相加的函数
    :param a: 数字1
    :param b: 数字2
    :return: 返回两个数相加的结果
    """
    return a + b

result = work6(1,2)
print(result)
print(work6.__doc__)
print(work6.__name__)